package com.znv.sentinelrepository;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author pengYongQiang
 * @version 1.0
 * @date 2020/7/11 10:52
 * 根据
 * znv.sentinel.metric.repository.metric-repository-type
 * 的配置，自动禁用一些自动配置
 * <p>
 * 1.类型为 MEMORY 时
 * 排除 DataSourceAutoConfiguration，MongoAutoConfiguration
 * 2.类型为 MYSQL 时
 * 排除 MongoAutoConfiguration
 * 3.类型为 MONGODB 时 //TODO
 * 排除 DataSourceAutoConfiguration
 */

public class AutoExcludeAutoConfigEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private final static Logger LOG = LoggerFactory.getLogger(AutoExcludeAutoConfigEnvironmentPostProcessor.class);

    //属性名称自动排除
    private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";

    // private static final String APPLICATION_YML_FILE_NAME = "applicationConfig: [classpath:/application.yml]";

    private static final String APPLICATION_PROPERTIES_FILE_NAME = "applicationConfig: [classpath:/application.properties]";

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {

        //自动配置排除列表
        List<String> autoConfigExcludeList = new ArrayList<>();
        /*
         * step1.获取 application.properties 配置文件的资源对象
         *       通过 是否启用自动排除自动配置属性判断，是否需要跳过
         *
         *       如果没有配置 znv.sentinel.metric.repository.metric-repository-type
         *       参数，则无需处理
         */
        OriginTrackedMapPropertySource propertySource = getOriginTrackedMapPropertySource(environment);
        if (isSkip(propertySource)) {
            return;
        }
        String properties_metricRepositoryType = (String) propertySource.getProperty(MetricRepositoryProperties.SENTINEL_REPOSITORY_TYPE);
        if (StringUtils.isBlank(properties_metricRepositoryType)) {
            return;
        }
        /*
         * step2.根据 znv.sentinel.metric.repository.metric-repository-type 配置的值
         *       确定哪些自动配置类需要排除。
         */
        if (MetricRepositoryType.MEMORY.name().equalsIgnoreCase(properties_metricRepositoryType)) {
            autoConfigExcludeList.add(DataSourceAutoConfiguration.class.getName());
            autoConfigExcludeList.add(MongoAutoConfiguration.class.getName());
        } else if (MetricRepositoryType.MYSQL.name().equalsIgnoreCase(properties_metricRepositoryType)) {
            autoConfigExcludeList.add(MongoAutoConfiguration.class.getName());
        } else if (MetricRepositoryType.MONGODB.name().equalsIgnoreCase(properties_metricRepositoryType)) {
            autoConfigExcludeList.add(DataSourceAutoConfiguration.class.getName());
        }
        /*
         * step3.应用排除列表到 spring
         */
        applyAutoConfigureExclude(propertySource, autoConfigExcludeList);
        LOG.info("\tAutoConfigureExclude :" + autoConfigExcludeList.toString());
    }

    private boolean isSkip(OriginTrackedMapPropertySource propertySource) {
        String property = (String) propertySource.getProperty(MetricRepositoryProperties.AUTO_EXCLUDE_AUTO_CONFIGURATION);
        Boolean properties_autoExcludeAutoConfiguration = Boolean.getBoolean(property);
        return properties_autoExcludeAutoConfiguration != null && !properties_autoExcludeAutoConfiguration;
    }

    private OriginTrackedMapPropertySource getOriginTrackedMapPropertySource(ConfigurableEnvironment environment) {
        MutablePropertySources propertySources = environment.getPropertySources();
        OriginTrackedMapPropertySource applicationPropertiesConfigPropertySource = (OriginTrackedMapPropertySource) propertySources.get(APPLICATION_PROPERTIES_FILE_NAME);
        if (applicationPropertiesConfigPropertySource == null) {
            throw new RuntimeException("not find file: " + APPLICATION_PROPERTIES_FILE_NAME);
        }
        return applicationPropertiesConfigPropertySource;
    }


    /**
     * 添加需要排除的自动配置类
     */
    private void applyAutoConfigureExclude(OriginTrackedMapPropertySource applicationConfigPropertySource, List<String> autoConfigExcludeList) {
        String excludes = StringUtils.join(autoConfigExcludeList, ",");
        String mapValue = null;
        if (applicationConfigPropertySource.containsProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE)) {
            mapValue = (String) applicationConfigPropertySource.getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE);
        }
        if (StringUtils.isBlank(mapValue)) {
            mapValue = excludes;
        } else {
            mapValue += "," + excludes;
        }
        Map<String, Object> source = applicationConfigPropertySource.getSource();
        source.put(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, mapValue);
    }
}
